﻿// Copyright (c) 2014 Converter Systems LLC

using ConverterSystems.Collections;
using ConverterSystems.ServiceModel;
using ConverterSystems.Workstation.Infrastructure;
using Microsoft.Practices.Prism.Commands;
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.ComponentModel.Composition;
using System.Diagnostics;

namespace ConverterSystems.Workstation.ViewModels
{
    /// <summary>
    /// A view model for MainView.
    /// </summary>
    [Export]
    public class MainViewModel : NavigationViewModelBase
    {
        private TestServerSession _session;
        private Subscription _subscription;

        [ImportingConstructor]
        public MainViewModel(TestServerSession session)
        {
            if (session == null) throw new ArgumentNullException("session");
            _session = session;
            // Create a new subscription, optionally specifying a faster publishing interval in milliseconds.
            _subscription = new Subscription(_session.DefaultSubscription) { PublishingInterval = 250 };
            // Configure this subscription with bindings to the properties specified with a [MonitoredItem] attribute.
            _subscription.WithModel(this).AddBindings()
                // You can add bindings for properties that do not have a [MonitoredItem] attribute.
                .AddBinding(m => m.Robot1Axis4, new MonitoredItem { StartNodeId = "ns=2;s=Robot1_Axis4" });
            // Add the new subscription to the session
            _session.AddSubscription(_subscription);

            // Build a command that writes the CommandParameter to a node of the server.
            Robot1ModeCommand = DelegateCommand<Int16>.FromAsyncHandler(async mode =>
                {
                    try
                    {
                        await _session.WriteValueAsync("ns=2;s=Robot1_Mode", mode);
                    }
                    catch (Exception ex)
                    {
                        Trace.TraceError("Error writing to NodeId {0} : {1}", "ns=2;s=Robot1_Mode", ex);
                    }
                });
            // Build a command that calls a method of the server.
            Robot1MultiplyCommand = DelegateCommand.FromAsyncHandler(async () =>
                {
                    try
                    {
                        // Place the input arguments into an object[], otherwise set inArgs = new object[0];.
                        var inArgs = new object[] { InputA, InputB };
                        // Call the method, passing the object node, method node, and input arguments.
                        var outArgs = await _session.CallAsync("ns=2;s=Robot1", "ns=2;s=Robot1_Multiply", inArgs);
                        // When the method returns, copy the output arguments back to the model.
                        Result = (double)outArgs[0];
                    }
                    catch (Exception ex)
                    {
                        Trace.TraceError("Error calling Robot1Multiply method: {0}", ex);
                    }
                });
            // Build a command to clear the form.
            ClearCommand = new DelegateCommand(() =>
                {
                    InputA = 0d;
                    InputB = 0d;
                    Result = 0d;
                });
        }

        public override void OnNavigatedTo(Microsoft.Practices.Prism.Regions.NavigationContext navigationContext)
        {
            if (_subscription == null || !_subscription.Created) return;
            _subscription.SetPublishingMode(true);
        }

        public override void OnNavigatedFrom(Microsoft.Practices.Prism.Regions.NavigationContext navigationContext)
        {
            if (_subscription == null || !_subscription.Created) return;
            _subscription.SetPublishingMode(false);
        }

        /// <summary>
        /// Gets or sets the value of Robot1Mode.
        /// </summary>
        [MonitoredItem(StartNodeId = "ns=2;s=Robot1_Mode")]
        public Int16 Robot1Mode
        {
            get { return _Robot1Mode; }
            set { SetProperty(ref _Robot1Mode, value); }
        }

        private Int16 _Robot1Mode;

        /// <summary>
        /// Gets or sets the value of Robot1Axis1.
        /// </summary>
        [MonitoredItem(StartNodeId = "ns=2;s=Robot1_Axis1")]
        public Single Robot1Axis1
        {
            get { return _Robot1Axis1; }
            set { SetProperty(ref _Robot1Axis1, value); }
        }

        private Single _Robot1Axis1;

        /// <summary>
        /// Gets or sets the value of Robot1Axis2.
        /// </summary>
        [MonitoredItem(StartNodeId = "ns=2;s=Robot1_Axis2")]
        public Single Robot1Axis2
        {
            get { return _Robot1Axis2; }
            set { SetProperty(ref _Robot1Axis2, value); }
        }

        private Single _Robot1Axis2;

        /// <summary>
        /// Gets or sets the value of Robot1Axis3.
        /// </summary>
        [MonitoredItem(StartNodeId = "ns=2;s=Robot1_Axis3")]
        public Single Robot1Axis3
        {
            get { return _Robot1Axis3; }
            set { SetProperty(ref _Robot1Axis3, value); }
        }

        private Single _Robot1Axis3;

        /// <summary>
        /// Gets or sets the value of Robot1Axis4.
        /// </summary>
        //[MonitoredItem(StartNodeId = "ns=2;s=Robot1_Axis4")]
        public Single Robot1Axis4
        {
            get { return _Robot1Axis4; }
            set { SetProperty(ref _Robot1Axis4, value); }
        }

        private Single _Robot1Axis4;

        /// <summary>
        /// Gets or sets the value of Robot1Speed.
        /// </summary>
        [MonitoredItem(StartNodeId = "ns=2;s=Robot1_Speed")]
        public Int16 Robot1Speed
        {
            get { return _Robot1Speed; }
            set { SetProperty(ref _Robot1Speed, value); }
        }

        private Int16 _Robot1Speed;

        /// <summary>
        /// Gets or sets the value of Robot1Laser.
        /// </summary>
        [MonitoredItem(StartNodeId = "ns=2;s=Robot1_Laser")]
        public Boolean Robot1Laser
        {
            get { return _Robot1Laser; }
            set { SetProperty(ref _Robot1Laser, value); }
        }

        private Boolean _Robot1Laser;

        /// <summary>
        /// Gets the recent history of the value of Robot1Axis1.
        /// </summary>
        /// <remarks>
        /// Filter sets conditions that limit the values deliverd. 'Stream' delivers every sample.
        /// CacheQueueSize sets the maximum number of values in the client queue. Older values are discarded.
        /// QueueSize sets the maximum number of values in the server queue. Older values are discarded. Used when publishing disabled.
        /// </remarks>
        [MonitoredItem(StartNodeId = "ns=2;s=Robot1_Axis1", Filter = DataChangeFilters.Stream, CacheQueueSize = 240, QueueSize = 240)]
        public ObservableQueue<DataValue> Robot1Axis1Queue
        {
            get { return _Robot1Axis1Queue; }
            private set { SetProperty(ref _Robot1Axis1Queue, value); }
        }

        private ObservableQueue<DataValue> _Robot1Axis1Queue = new ObservableQueue<DataValue>();

        /// <summary>
        /// Gets the event of Robot1.
        /// </summary>
        /// <remarks>
        /// AttributeId must be set to 'EventNotifier'.
        /// Filter sets the fields that will be delivered with each event.
        /// Also, Filter sets conditions that limit the events delivered, such as Source, Severity etc.
        /// The default Filter delivers all fields from BaseEventType
        /// </remarks>
        [MonitoredItem(StartNodeId = "ns=2;s=Robot1", AttributeId = Attributes.EventNotifier, Filter = EventFilters.Minimal)]
        public EventFieldList Robot1
        {
            get { return _Robot1; }
            private set { SetProperty(ref _Robot1, value); }
        }

        private EventFieldList _Robot1;

        /// <summary>
        /// Gets the command to write the value of Robot1Mode.
        /// </summary>
        public DelegateCommand<Int16> Robot1ModeCommand { get; private set; }

        /// <summary>
        /// Gets or sets the value of InputA.
        /// </summary>
        public double InputA
        {
            get { return _InputA; }
            set { SetProperty(ref _InputA, value); }
        }

        private double _InputA;

        /// <summary>
        /// Gets or sets the value of InputB.
        /// </summary>
        public double InputB
        {
            get { return _InputB; }
            set { SetProperty(ref _InputB, value); }
        }

        private double _InputB;

        /// <summary>
        /// Gets or sets the value of Result.
        /// </summary>
        public double Result
        {
            get { return _Result; }
            set { SetProperty(ref _Result, value); }
        }

        private double _Result;

        /// <summary>
        /// Gets the command to call the method named Robot1Multiply.
        /// </summary>
        public DelegateCommand Robot1MultiplyCommand { get; private set; }

        /// <summary>
        /// Gets the command to clear the form.
        /// </summary>
        public DelegateCommand ClearCommand { get; private set; }
    }
}